package com.chouren.goandroid.img;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;
import android.widget.ImageView;

public class ImageLoader {
	private static final String TAG = "ImageLoader";
	private MemoryCache memoryCache;
	private FileCache fileCache;
	private ExecutorService executorService;
	private Map<ImageView, String> imageViews = Collections
			.synchronizedMap(new WeakHashMap<ImageView, String>());

	public ImageLoader(Context context) {
		fileCache = new FileCache(context);
		memoryCache = new MemoryCache();
		executorService = Executors.newFixedThreadPool(1);
	}

	public void loadImage(String url, ImageView imageView,
			boolean isOnlyLoadFromCache) {
		Log.d(TAG, "load image url:" + url);
		Bitmap bitmap = memoryCache.getCache(url);

		if (bitmap != null) {
			Log.d(TAG, "load from memory");
			imageView.setImageBitmap(bitmap);
		} else {
			Log.d(TAG, "load from dir");
			bitmap = decodeBitmapFromFile(fileCache.getCacheFile(url));
		}

		if (bitmap == null && !isOnlyLoadFromCache) {
			ImageLoad imageLoad = new ImageLoad(url, imageView);
			if (needStartNewThread(imageLoad)) {
				Log.d(TAG, "load from net");
				newThreadToLoadImage(imageLoad);
			} else {
				Log.d(TAG, "image is loading, cancel, url:" + url);
			}
			imageViews.put(imageView, url);
		} else {
			imageView.setImageBitmap(bitmap);
		}
	}

	private void newThreadToLoadImage(ImageLoad imageLoad) {
		executorService.submit(new ImageLoadTask(imageLoad));
	}

	private boolean needStartNewThread(ImageLoad imageLoad) {
		boolean result = true;
		ImageView iv = imageLoad.getImageView();
		if (iv != null) {
			String url = imageViews.get(iv);
			if (url != null && url.equals(imageLoad.getUrl())) {
				result = false;
			}
		}
		return result;
	}

	private Bitmap decodeBitmapFromFile(File file) {
		if (file == null) {
			return null;
		}
		try {
			// decode image size
			BitmapFactory.Options option = new BitmapFactory.Options();
			option.inJustDecodeBounds = true;
			BitmapFactory.decodeStream(new FileInputStream(file), null, option);

			// Find the correct scale value. It should be the power of 2.
			final int REQUIRED_SIZE = 100;
			int width_tmp = option.outWidth, height_tmp = option.outHeight;
			int scale = 1;
			while (true) {
				if (width_tmp / 2 < REQUIRED_SIZE
						|| height_tmp / 2 < REQUIRED_SIZE) {
					break;
				}
				width_tmp /= 2;
				height_tmp /= 2;
				scale *= 2;
			}

			// decode with inSampleSize
			BitmapFactory.Options option2 = new BitmapFactory.Options();
			option2.inSampleSize = scale;
			return BitmapFactory.decodeStream(new FileInputStream(file), null,
					option2);

		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	private class ImageLoadTask implements Runnable {
		private ImageLoad imageLoad;

		public ImageLoadTask(ImageLoad imageLoad) {
			this.imageLoad = imageLoad;
		}

		@Override
		public void run() {
			try {
				Bitmap bitmap = null;
				if (!fileCache.createCacheFile(imageLoad.getUrl())) {
					return;
				}
				File f = fileCache.getCacheFile(imageLoad.getUrl());
				URL imageUrl = new URL(imageLoad.getUrl());
				HttpURLConnection conn = (HttpURLConnection) imageUrl
						.openConnection();
				conn.setConnectTimeout(30000);
				conn.setReadTimeout(30000);
				conn.setInstanceFollowRedirects(true);
				InputStream is = conn.getInputStream();
				OutputStream os = new FileOutputStream(f);
				CopyStream(is, os);
				bitmap = decodeBitmapFromFile(f);
				memoryCache.put(imageLoad.getUrl(), bitmap);
				Activity activity = (Activity) imageLoad.getImageView()
						.getContext();
				activity.runOnUiThread(new BitmapDisplayTask(bitmap, imageLoad));
			} catch (Exception ex) {
				ex.printStackTrace();
			}
		}

		public void CopyStream(InputStream is, OutputStream os) {
			final int buffer_size = 1024;
			try {
				byte[] bytes = new byte[buffer_size];
				for (;;) {
					int count = is.read(bytes, 0, buffer_size);
					if (count == -1)
						break;
					os.write(bytes, 0, count);
				}
			} catch (Exception ex) {
				ex.printStackTrace();
			} finally {
				if(is != null) {
					try {
						is.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
				if(os != null) {
					try {
						os.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}

	public interface OnImageDownloadListener {
		public void onImageDownloadFinish(String url, Bitmap bitmap);
	}

	class BitmapDisplayTask implements Runnable {
		private Bitmap bitmap;
		private ImageLoad imageLoad;

		public BitmapDisplayTask(Bitmap bitmap, ImageLoad imageLoad) {
			this.bitmap = bitmap;
			this.imageLoad = imageLoad;
		}

		@Override
		public void run() {
			if (bitmap != null) {
				imageLoad.getImageView().setImageBitmap(bitmap);
			}
		}
	}
}
